package com.njupt.yanglonglong.report.demo.server;

import com.njupt.yanglonglong.report.demo.entity.DeviceApplied;
import com.njupt.yanglonglong.report.demo.service.DeviceAppliedService;
import com.njupt.yanglonglong.report.demo.utils.UUIDUtil;
import com.njupt.yanglonglong.report.demo.utils.websocket.BaseData;
import com.njupt.yanglonglong.report.demo.utils.websocket.WsTools;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @className: SocketServer
 * @Description: TODO test
 * @author: lpl
 * @date: 2021/5/13 17:23
 */
@ServerEndpoint(value = "/socketserver")
@Component
@Slf4j
@Data
public class SocketServer {


    public static DeviceAppliedService deviceAppliedService;


    private static Map<String, SocketServer> AllChat = new ConcurrentHashMap<>();
    private String deviceId = null;
    private Session session = null;

    //文件使用
    private FileInputStream in = null;
    private FileChannel file = null;
    public boolean fileState = false;

    //烧录使用
    private StringBuilder flashMessage = new StringBuilder();

    //流媒体使用
    private String mediaUrl = null;
    private static String rtmp = "rtmp://39.98.126.208:1935";


    public static SocketServer getChatByDeviceId(String devId) {
        System.out.println(devId);
        SocketServer t = AllChat.get(devId);
        System.out.println(t);
        for (Map.Entry<String, SocketServer> entry : AllChat.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        return t;
    }

    @OnError
    public void onError(Session session, Throwable t) throws IOException {
        System.out.println("连接出现错误。。。");
        t.printStackTrace();
        fileClose();
        deviceAppliedService.removeById(deviceId);
        AllChat.remove(deviceId);
        if (session.isOpen()) {
            session.close();
        }
    }

    @OnOpen
    public void onOpen(Session session) {
        log.info("硬件连接已建立");
        this.session = session;
    }

    @OnClose
    public void onClose() {
        log.info("硬件连接已关闭");
        fileClose();
        deviceAppliedService.removeById(deviceId);
        AllChat.remove(deviceId);
    }

    @OnMessage
    public void OnMessage(byte[] message) {
        log.info("连接收到消息 {}", message);
        try {
            BaseData data = WsTools.takeSendText(message);
            Action_judgment(data);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void Action_judgment(BaseData data) {
        log.info("Action_judgment进行消息转发 data:{}", data);
        switch (data.getType()) {
            case 1:
                //type = 1为注册流程
                log.info("进入注册流程。。。");
                DeviceRegSer(data);
                break;
            case 2:
                //type = 2为文件流程
                log.info("进入发送文件流程。。。");
                SendFileSer(data);
                break;
            case 3:
                //type=3进入烧录流程
                log.info("进入烧录流程。。。");
                flashService(data);
                break;
            case 4:
                //type = 4 推流
                log.info("进入流媒体流程。。。");
                mediaUrlService(data);
                break;
            default:
                break;
        }
    }

    private void fileClose() {
        try {
            if (in != null) {
                in.close();
                in = null;
            }
            if (file != null) {
                file.close();
                file = null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 开始发送文件
     *
     * @param filename 文件路径
     */
    public void SendFileBeg(String filename) {

        fileClose();
        log.info("开始传送文件 文件名{}", filename);
        try {
            in = new FileInputStream(filename);
            file = in.getChannel();
            System.out.println(in);
            System.out.println(file);
            BaseData data = new BaseData();
            data.setCtrl((byte) 2);
            data.setType((byte) 2);
            data.setLength_total((int) file.size());
            data.setNumber((short) 0);
            data.setLength_data((short) 0);

            ByteBuffer buffer = WsTools.makeSendText(data);
            session.getBasicRemote().sendBinary(buffer);
            System.out.println("SendFileBeg消息已发送。。。");

        } catch (Exception e) {
            e.printStackTrace();
            fileClose();
        }
    }

    /**
     * 开始发送文件
     *
     * @param multipartFile 文件类
     */
    public void SendFileBeg(MultipartFile multipartFile) {
        fileClose();
        log.info("开始传送文件 文件名{}", multipartFile.getOriginalFilename());
        try {
//            byte[] byteArr = multipartFile.getBytes();
            in = (FileInputStream) multipartFile.getInputStream();
            file = in.getChannel();
            log.info("SendFileBeg取到in{} file{}", in, file);
            BaseData data = new BaseData();
            data.setCtrl((byte) 2);
            data.setType((byte) 2);
            data.setLength_total((int) file.size());
            data.setNumber((short) 0);
            data.setLength_data((short) 0);
            ByteBuffer buffer = WsTools.makeSendText(data);
            session.getBasicRemote().sendBinary(buffer);
            log.info("SendFileBeg消息已发送。。。");
        } catch (Exception e) {
            e.printStackTrace();
            fileClose();
        }
    }

    public void flashBegin() {
        try {
            BaseData data = new BaseData();
            data.setCtrl((byte) 2);
            data.setType((byte) 3);
            data.setLength_total((int) 0);
            data.setNumber((short) 0);
            data.setLength_data((short) 0);
            ByteBuffer buffer = WsTools.makeSendText(data);
            log.info("发起烧录流程flashBegin,发送的数据: {}", data);
            session.getBasicRemote().sendBinary(buffer);
        } catch (Exception e) {
            e.printStackTrace();
            fileClose();
        }
    }

    /**
     * 文件后进行烧录
     */
    public void flashService(BaseData baseData) {
        log.info("flashBegin收到 {}", baseData);
        if ((baseData.getCtrl() & 1) == 0) {
            switch (baseData.getCtrl() >> 1) {
                case 1:
                    log.info("对方回复了烧录请求");
                    flashMessage.append("Hardwire is flashing...<br>");
                    //同时开启推流流程
                    mediaServiceBegin();
                    break;
                case 2:
                    String string = new String(baseData.getData(), StandardCharsets.US_ASCII)
                            .substring(0, baseData.getLength_data() - 1);
                    flashMessage.append(string + "<br>");
                    log.info("打印出message {}", flashMessage);
                    try {
                        BaseData datasend = new BaseData();
                        //0100
                        datasend.setCtrl((byte) 4);
                        datasend.setType((byte) 3);
                        datasend.setLength_total((int) 0);
                        datasend.setNumber((short) 0);
                        datasend.setLength_data((short) 0);

                        ByteBuffer buffer = WsTools.makeSendText(datasend);
                        session.getBasicRemote().sendBinary(buffer);
                        log.info("消息已发送 {}", datasend);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    }


    private void DeviceRegSer(BaseData data) {
        log.info("DeviceRegSer接收到data:{}", data);
        //ctrl最低位是否为0表示是否成功
        if ((data.getCtrl() & 1) == 0) {
            //判断小流程
            switch (data.getCtrl() >> 1) {
                //接收到发起注册报文
                case 1:
                    try {
                        BaseData datasend = new BaseData();
                        //0010
                        datasend.setCtrl(data.getCtrl());
                        datasend.setType(data.getType());
                        datasend.setLength_total((int) 0);
                        datasend.setNumber((short) 0);
                        datasend.setLength_data((short) 0);

                        ByteBuffer buffer = WsTools.makeSendText(datasend);
                        session.getBasicRemote().sendBinary(buffer);
                        log.info("消息已发送 {}", datasend);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
                //接收到注册内容报文
                case 2:
                    if (data.getNumber() == 0) {
                        byte[] temp = new byte[data.getLength_data()];
                        //拿到data
                        System.arraycopy(data.getData(), 0, temp, 0, data.getLength_data());
                        deviceId = UUIDUtil.get32UUID();
                        String deviceType = new String(temp);
                        log.info("[设备注册]id号为：{} 设备类型：{}", deviceId, deviceType);
                        deviceAppliedService.save(new DeviceApplied(deviceId, deviceType));
                        AllChat.put(deviceId, this);
                        try {
                            BaseData dataSend = new BaseData();
                            dataSend.setCtrl(data.getCtrl());
                            dataSend.setType(data.getType());
                            dataSend.setLength_total((int) 0);
                            dataSend.setNumber((short) 0);
                            dataSend.setLength_data((short) 0);
                            ByteBuffer buffer = WsTools.makeSendText(dataSend);
                            session.getBasicRemote().sendBinary(buffer);
                            log.info("buffer消息已发送。。。");
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } else {
                        log.error("DeviceRegSer接收到了多个注册报文内容");
                    }
                    break;
            }
        } else {
            //注册流程失败，跳到目前流程进行重发 todo
            log.error("接收到失败报文");
        }
    }

    private void SendFileSer(BaseData data) {
        log.info("SendFileSer接收到 data: {}", data);
        if ((data.getCtrl() & 1) == 0) {
            log.info("成功进入");
            switch (data.getCtrl() >> 1) {
                case 2:
                    log.info("SendFileSer成功进入case 2");
                    BaseData dataSend = new BaseData();
                    try {
                        if (file.size() == file.position()) {
                            System.out.println("文件发送结束！！！！");
                            fileClose();
                            //进入烧录流程
                            flashBegin();
                            //文件上传成功标志
                            this.fileState = true;
                            return;
                        }
                        dataSend.setCtrl((byte) 4);
                        dataSend.setType((byte) 2);
                        dataSend.setLength_total((int) file.size());
                        dataSend.setNumber((short) (file.position() / (BaseData.DATACLASS_LENGTH - BaseData.HEADER_LENGTH)));
                        if ((file.size() - file.position()) >= (BaseData.DATACLASS_LENGTH - BaseData.HEADER_LENGTH)) {
                            dataSend.setLength_data((short) (BaseData.DATACLASS_LENGTH - BaseData.HEADER_LENGTH));
                        } else {
                            dataSend.setLength_data((short) (file.size() - file.position()));
                        }
                        ByteBuffer buffer;
                        buffer = WsTools.makeSendText(dataSend);
                        buffer.position(8);
                        file.read(buffer);
                        buffer.position(0);
                        session.getBasicRemote().sendBinary(buffer);
                        log.info("文件字节流信息已发送 {}", buffer);
                    } catch (Exception e) {
                        e.printStackTrace();
                        fileClose();
                    }
                    break;
                default:
                    break;
            }
        } else {
            log.error("文件发送出错");
        }
    }

    /**
     * 发送文件完成报文
     */
    private void sendFileFinish() {
        try {
            BaseData baseData = new BaseData();
            baseData.setCtrl((byte) 0);
            baseData.setType((byte) 2);
            baseData.setLength_total(0);
            baseData.setNumber((short) 0);
            baseData.setLength_data((short) 0);

            ByteBuffer buffer = WsTools.makeSendText(baseData);
            session.getBasicRemote().sendBinary(buffer);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void mediaServiceBegin() {
        log.info("[进入推流流程]");
        BaseData data = new BaseData();
        data.setCtrl((byte) 2);
        data.setType((byte) 4);
        data.setLength_total((int) 0);
        data.setNumber((short) 0);
        data.setLength_data((short) 0);
        ByteBuffer buffer = WsTools.makeSendText(data);
        try {
            session.getBasicRemote().sendBinary(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private void mediaUrlService(BaseData data) {
        log.info("mediaUrlService接收到数据 data:{}", data);
        if ((data.getCtrl() & 1) == 0) {
            switch (data.getCtrl() >> 1) {
                case 1:
                    //进入发送url流程
                    rtmp = rtmp + "\0";
                    BaseData dataSend = new BaseData();
                    //0100
                    dataSend.setCtrl((byte) 4);
                    dataSend.setType((byte) 4);
                    dataSend.setLength_total((rtmp.length() + BaseData.HEADER_LENGTH));
                    dataSend.setNumber((short) 0);
                    dataSend.setLength_data((short) rtmp.length());
                    dataSend.setData(rtmp.getBytes());
                    ByteBuffer buffer = WsTools.makeSendText(dataSend);
                    try {
                        session.getBasicRemote().sendBinary(buffer);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    break;
                case 2:
                    //进入解析返回的url流程
                    byte[] temp = new byte[data.getLength_data()];
                    //拿到data
                    System.arraycopy(data.getData(), 0, temp, 0, data.getLength_data());
                    this.mediaUrl = new String(temp);
                    log.info("推流地址为 mediaUrl: {}", mediaUrl);
                    break;
            }
        }
    }
}
